﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Web.UI;

using Swaf;
using Swaf.BizObj;
using Swaf.Container;
using Swaf.Security;
using Swaf.Gui;
using Swaf.Gui.AspNet2;

namespace Syrinx.Gui.AspNet.Ajax
{
	public class Menu : Syrinx.Gui.AspNet.Menu, IAjaxClientControl
	{
		protected static string s_setupScript = @"
var c = new ClientControl(document.getElementById('{1}'), setMenuState, getMenuState);
c.id = '{1}';
{2}
{0}BizProc.addClientControl(c);
";
		protected static string s_globalScript = @"
function setMenuState(stateEl)
{
	try
	{
		var html = stateEl.getAttribute('html');
		if(html != null && html != '')
		{
			$('#'+this.id).replaceWith(html);
			setupMenuEvents();
		}
	}catch(e){}
}
function getMenuState(stateEl)
{
	
}
";
		protected IApplication m_app;
		protected bool m_createActiveOnLoad = true;
		protected int m_ajaxRefreshRate = -1;
        protected ActiveBizProc m_bizProc = null;

		public Menu()
		{
		}

		public Menu(ActiveWidget widget)
		{
			ID = widget.State.Id;
		}

		public int AjaxRefreshRate { get { return m_ajaxRefreshRate; } set { m_ajaxRefreshRate = value; } }

		protected IApplication App
		{
			get
			{
				if (m_app == null)
					m_app = Swaf.Application.currentApp;
				return m_app;
			}
		}

		protected override string getMenuItemHtml(bool includeImgSpan, MenuItem mi)
		{
			return (mi.Text != null) ? App.resMgr.resolve(mi.getMenuItemHtml(includeImgSpan)) : null;
		}

		protected override string getMenuItemNavUrl(MenuItem mi)
		{
            string navUrl = base.getMenuItemNavUrl(mi);// mi.getCurrentNavigateUrl();
			if (navUrl != null)
			{
				if (navUrl.IndexOf(':') == -1 && !navUrl.StartsWith("$CmpSetting(sitePath)$"))
					navUrl = App.resMgr.resolve("$CmpSetting(sitePath)$" + navUrl);
				else
					navUrl = App.resMgr.resolve(navUrl);
			}
			return navUrl;
		}

		protected override void Render(HtmlTextWriter writer)
		{
			State.saveControlState(this);
			State.State["Items"] = Items;
			if (!(Page is NitobiBasePage) || !((NitobiBasePage)Page).IsAjaxEvent)
				base.Render(writer);
		}

		protected override void OnLoad(EventArgs e)
		{
			WidgetOfflineState offlineState = State;
			if (m_createActiveOnLoad)
			{
				ActiveWidget act = new ActiveWidget(this, BPC, offlineState);
				act.CreatedFromPage = false;
				BPC.defineActiveWidget(act);
			}
            ActiveBizProc bizProc = BizProc;
			base.OnLoad(e);

			Page.ClientScript.RegisterStartupScript(typeof(Menu), "MenuGlobal", s_globalScript, true);
			string refreshScript = "";
			if(AjaxRefreshRate > 0)
				refreshScript = string.Format(@"c.refreshTimer = window.setInterval('if(!menuItemOpen){0}BizProc.triggerEvent(""{1}"",""refresh"", """",null,false)',{2});", ParentBizProcIds, WidgetId, AjaxRefreshRate);
					
			Page.ClientScript.RegisterStartupScript(typeof(Menu), WidgetId,
				string.Format(s_setupScript, ParentBizProcIds, WidgetId, refreshScript), true);
		}

		public BizProcCoordinator BPC
		{
			get { return (Page is IPage) ? ((IPage)Page).BPC : null; }
		}

		protected WidgetOfflineState State
		{
			get
			{
				WidgetOfflineState state = BPC.getWidgetOfflineState(WidgetId);
				if (state == null)
				{
					BPC.addWidgetOfflineState(state = new WidgetOfflineState(WidgetId, this.GetType().AssemblyQualifiedName));
					state.BizProcWidgetType = BizProcWidgetType;
					state.setParentBizProcIds(ParentBizProcIds);
				}
				return state;
			}
		}

        public ActiveBizProc BizProc
        {
            get
            {
                if (m_bizProc == null)
                {
                    m_bizProc = BPC.getActiveBizProc(ParentBizProcIds);
                    if (m_bizProc == null)
                    {
                        BizProcOfflineState bps = new BizProcOfflineState(ParentBizProcIds);
                        bps.addParentBizProcInfo(new ParentBizProcInfo("loginBP", "loginBP", null, null, false, false));
                        bps.FactoryName = "smenu";
                        BPC.addBizProcOfflineState(bps);
                        m_bizProc = BPC.getActiveBizProc(ParentBizProcIds);
                        SyrinxMenuBizProc smbc = new SyrinxMenuBizProc(m_bizProc);
                    }
                }
                return m_bizProc;
            }
        }

		#region IAjaxClientControl Members

		public bool CreateOnLoad
		{
			set { m_createActiveOnLoad = value; }
		}

		public void setState(System.Xml.XmlElement clientState)
		{
			State.getControlState(this);
			m_items = (List<MenuItem>)State.State["Items"];
		}

		public void getState(System.Xml.XmlElement clientState, string pageHtml)
		{
			StringWriter sw = new StringWriter();
			using (HtmlTextWriter writer = new HtmlTextWriter(sw))
			{
				renderMenuArea(writer, Items, Orientation, NumLayers, CssClass, ItemNumLayers, ItemCssClass, ClientID);
				string html = sw.ToString();
				clientState.SetAttribute("html", html);
			}
		}

		public void processAjaxEvent(System.Xml.XmlElement eventInfo)
		{
            processAjaxEvent(eventInfo, Items);
		}

        protected void processAjaxEvent(System.Xml.XmlElement eventInfo, List<MenuItem> items)
        {
            string id = eventInfo.GetAttribute("event");
            foreach (MenuItem mi in items)
                if (mi is ActionMenuItem && string.Compare(((ActionMenuItem)mi).Id, id) == 0)
                    ((ActionMenuItem)mi).triggerEvent();
                else if(mi.Items.Count > 0)
                    processAjaxEvent(eventInfo, mi.Items);
        }

		public bool NeedToGenerateHtmlWithServerControls
		{
			get { return false; }
		}

		#endregion

		#region IWidget Members

		public Swaf.Gui.BizProcWidgetType BizProcWidgetType
		{
			get
			{
				return Swaf.Gui.BizProcWidgetType.other;
			}
			set
			{
				//Intentionally not implemented
			}
		}

		public string ParentBizProcIds
		{
			get
			{
				return "SyrinxMenu";
			}
			set
			{
				//Intentionally not implemented
			}
		}

		public string WidgetId
		{
			get { return ClientID; }
		}

		#endregion
	}

	public class SyrinxMenuBizProc : BaseBusinessProcess
	{
		protected IList<object> m_childCtrls = null;
		public SyrinxMenuBizProc(ActiveBizProc procState)
            :base(procState)
        {
        }

		public override Privileges CurrentEditPrivilege { get { return Privileges.Delete; } }
		protected override void setupRuleNames(){}
		public override void showData(ForceLevel level)
		{
			if(m_childCtrls == null)
				m_childCtrls = m_procState.getChildControls(BizProcWidgetType.other);
		}
	}

	[Serializable]
	public class SecureMenuItem : Syrinx.Gui.AspNet.MenuItem
	{
		protected string m_securityAppFunction;
		protected Privileges m_privLevel = Swaf.Security.Privileges.ReadOnly;
		protected bool m_mustBeAuthenticated = false;
        protected bool m_authenticatedOptionSet = false;

		public string AppFunction { get { return m_securityAppFunction; } set { m_securityAppFunction = value; } }
		public Privileges PrivilegeLevel { get { return m_privLevel; } set { m_privLevel = value; } }
        public bool MustBeAuthenticated { get { return m_mustBeAuthenticated; } set { m_mustBeAuthenticated = value; m_authenticatedOptionSet = true; } }

		public override bool Visible
		{
			get
			{
				bool isVisible = true;
				if (!Cmn.IsEmpty(AppFunction))
				{
					Privileges level = Swaf.Application.currentApp.securityMgr.currentPrincipal.getPrivilege(AppFunction);
					isVisible = level >= PrivilegeLevel;
				}
                bool userAuthenticated = Cmn.IsTrue(Swaf.Application.currentApp.securityMgr.currentPrincipal.additionalInfo.get("authenticated"));
				if ((MustBeAuthenticated && !userAuthenticated) ||
                    (!MustBeAuthenticated && m_authenticatedOptionSet && userAuthenticated))
					isVisible = false;

                return isVisible;
			}
		}
	}

	[Serializable]
	public class ArticleListMenuItem : Syrinx.Gui.AspNet.MenuItem
	{
		protected string m_pid;
		protected string m_navLink;
		protected int m_maxElements = -1;
		protected string m_treeName = null;
		protected bool m_entireTree = false;
		protected bool m_includeSelf = true;
		protected string m_itemTemplate = null;
        protected bool m_shareCssClassWithChildItems = false;

		protected List<Articles.Gui.AspNet.FilterOption> m_filters = new List<Articles.Gui.AspNet.FilterOption>();
		protected List<Articles.Gui.AspNet.SortOption> m_orderBy = new List<Articles.Gui.AspNet.SortOption>();

		public ArticleListMenuItem()
		{			
		}

		public string ArticleId { get { return m_pid; } set { m_pid = value; } }
		public int MaxElements { get { return m_maxElements; } set { m_maxElements = value; } }
		public string TreeName { get { return m_treeName; } set { m_treeName = value; } }
		public string NavLink { get { return m_navLink; } set { m_navLink = value; } }
		public bool EntireTree { get { return m_entireTree; } set { m_entireTree = value; } }
        public bool IncludeSelf { get { return m_includeSelf; } set { m_includeSelf = value; } }
        public bool ShareCssClassWithChildItems { get { return m_shareCssClassWithChildItems; } set { m_shareCssClassWithChildItems = value; } }

		[PersistenceModeAttribute(PersistenceMode.InnerProperty)]
		public string Template { get { return m_itemTemplate; } set { m_itemTemplate = value; } }

		[PersistenceModeAttribute(PersistenceMode.InnerProperty)]
		public List<Articles.Gui.AspNet.FilterOption> Filter { get { return m_filters; } }

		[PersistenceModeAttribute(PersistenceMode.InnerProperty)]
		public List<Articles.Gui.AspNet.SortOption> Sort { get { return m_orderBy; } }

		protected IBizObjList getArticles()
		{
			Swaf.Container.FlexiMap m = new Swaf.Container.FlexiMap();
			m.put("parentId", ArticleId);
			m.put("listDetailLevel", DetailLevel.basicDetails);
			m.put("includeSelf", IncludeSelf);
			m.put("includeCounts", true);
			m.put("stripLeading", true);
			m.put("entireTree", EntireTree);
			m.put("resetDepth", true);
			if (!Cmn.IsEmpty(TreeName))
				m.put("objectTreeName", TreeName);
			if (MaxElements > 0)
				m.put("maxElements", MaxElements);
			IResults rc = Swaf.Application.currentApp.ruleMgr.execute("getArticles", m, 
					Articles.Gui.AspNet.FilterSortOption.createFilterSortOption(Filter, Sort));
			return rc.CallResults as IBizObjList;
		}

        public override void setupMenuItem(Syrinx.Gui.AspNet.Menu parentMenu)
		{
			m_items = new List<MenuItem>();
            base.setupMenuItem(parentMenu);
			Stack<List<MenuItem>> menuStack = new Stack<List<MenuItem>>();

			List<MenuItem> curItems = Items;

			MenuItem curItem = null;
			int curDepth = 0;

			IBizObjList articles = getArticles();
            if (articles == null)
                return;

            Stack<IBizObj> parent = new Stack<IBizObj>();
			NameAddressSupportDictionaryWrapper articleWrapper = new NameAddressSupportDictionaryWrapper(null);
			for(int pos = 0; pos < articles.numElements; pos++)
			{
				IBizObj article = articles[pos];
				articleWrapper.InnerElement = article;

				int depth = (int)article.get("depth");
                if (depth > curDepth && pos > 0)
                    parent.Push(articles[pos - 1]);
                else if (depth < curDepth)
                    parent.Pop();

                //--- Used when building a tree of sub menus based on article tree depth
                if (ChildLayout != ChildLayoutType.AsSiblings && curItem != null)
                    if (depth > curDepth)
                    {
                        menuStack.Push(curItems);
                        curItems = curItem.Items;
                    }
                    else if (depth < curDepth)
                        curItems = menuStack.Pop();
                // -----------------------------------

				string pageJump = (string)article.get("readMoreJump");
				if (Cmn.IsEmpty(pageJump))
				{
					if (NavLink != null)
					{
						pageJump = Swaf.Application.currentApp.resMgr.resolve(NavLink, articleWrapper);
					}
					else
					{
						string pName = "";
						if (parent.Count > 0)
							pName = parent.Peek().get("uniqueId") + "_";
						pageJump = "/articles/" + pName + article.get("uniqueId") + ".aspx";
					}
				}
				if (curItem == null)
				{
					curItem = this;
					if (Cmn.IsEmpty(Text))
						Text = (string)article.get("title");
					if (NavigateUrl == null)
						NavigateUrl = pageJump;
					if (!IncludeSelf)
						pos--;
					continue;
				}
				MenuItem mi = new MenuItem((string)article.get("title"), pageJump);
				if (!Cmn.IsEmpty(Template))
					mi.Text = Swaf.Application.currentApp.resMgr.resolve(Template, articleWrapper);
                if (ShareCssClassWithChildItems)
                    mi.CssClass = CssClass;

				curItems.Add(mi);
				curItem = mi;
				curDepth = depth;
			}
		}
	}

    [Serializable]
    public class ActionMenuItem : Syrinx.Gui.AspNet.MenuItem
    {
        protected Menu m_parent = null;
        protected string m_actions = null;
        protected string m_id = null;

        public string Actions { get { return m_actions; } set { m_actions = value; } }
        public string Id { get { return m_id; } set { m_id = value; } }

        public override bool Visible
        {
            get
            {
                bool visible = false;
                if (m_parent != null)
                {
                    List<IAction> actions = BPC.getBizProcActions(Actions);
                    ActionAvailability av = ActionAvailability.none;
                    if (actions != null && actions.Count > 0)
                        av = actions[0].Availability;
                    visible = (av == ActionAvailability.enabled);
                }
                return visible;
            }
        }

        public void triggerEvent()
        {
            string[] actionNames = Actions.Split(',');
            foreach (string actionName in actionNames)
            {
                object trigParam = null;
                IAction action = BPC.getBizProcAction(actionName, ref trigParam);
                //if (popupTrigParam != null && trigParam == null)
                //    trigParam = popupTrigParam;
                if (action != null && action.Enabled)
                    if (trigParam == null)
                    {
                        if (action.trigger())
                            break;
                    }
                    else
                        action.trigger(trigParam);
            }
        }

        public override void onLoadMenuSetup(Syrinx.Gui.AspNet.Menu parent)
        {
            base.onLoadMenuSetup(parent);
            if (parent is Menu)
                m_parent = (Menu)parent;
        }

        public override string getCurrentNavigateUrl()
        {
            if(m_parent != null)
                return string.Format(@"javascript:{0}BizProc.triggerEvent(""{1}"",""{2}"");",
                    m_parent.BizProc.BizProc.Id, m_parent.WidgetId, Id);
            return string.Format("javascript;");
        }

        protected BizProcCoordinator BPC
        {
            get
            {
                BizProcCoordinator bpc = null;
                if (m_parent != null && m_parent.Page is IPage)
                    bpc = ((IPage)m_parent.Page).BPC;
                return bpc;
            }
        }
    }

    [Serializable]
    public class BizProcMenuItem : SecureMenuItem
    {
        protected BizProc m_bizProc;
        protected string m_bizProcId;
        protected string m_factoryName;
        protected bool m_refreshOnSelect = false;
        protected string m_itemTemplate = null;

        public string BizProcId { get { return m_bizProcId; } set { m_bizProcId = value; } }
        public string FactoryName { get { return m_factoryName; } set { m_factoryName = value; } }
        public bool RefreshOnSelect { get { return m_refreshOnSelect; } set { m_refreshOnSelect = value; } }

        [PersistenceModeAttribute(PersistenceMode.InnerProperty)]
        public string Template { get { return m_itemTemplate; } set { m_itemTemplate = value; } }

        public override void onLoadMenuSetup(Syrinx.Gui.AspNet.Menu parent)
        {
            base.onLoadMenuSetup(parent);

            m_bizProc = new BizProc();
            m_bizProc.Page = parent.Page;
            m_bizProc.ID = BizProcId;
            m_bizProc.FactoryName = FactoryName;

            //Ensure the bizproc is created so that it is register in the page scripting properly
            IBusinessProcess bp = m_bizProc.TheBizProc;
        }

        public override void setupMenuItem(Syrinx.Gui.AspNet.Menu parent)
        {
            m_items = new List<MenuItem>(); 
            NameAddressSupportDictionaryWrapper bizObjWrapper = new NameAddressSupportDictionaryWrapper(null);
            IBizObjList bizobjs = m_bizProc.TheBizProc.SourceBizObjs;
            if (bizobjs != null)
                foreach (IBizObj bo in bizobjs)
                {
                    bizObjWrapper.InnerElement = bo;
                    MenuItem mi = new MenuItem(
                        Swaf.Application.currentApp.resMgr.resolve(Template, bizObjWrapper),
                        getItemSelectScript(bo));
                    Items.Add(mi);
                }
        }

        protected string getItemSelectScript(IBizObj item)
        {
            string afterEvent = "";
            if (RefreshOnSelect)
                afterEvent = ", function(){{window.location.href=window.location.href;}}";
            return string.Format("javascript: {0}BizProc.triggerEvent('{0}', 'selectBizObj', '{1}'{2});",
                BizProcId, item.KeyValue, afterEvent);
        }

    }
}
